#!/usr/bin/env python
#
#===- check_refactor_tool.py - Validate refactoring tools ----*- python -*--===#
#
# ==============================================================================
# LLVM Release License
# ==============================================================================
# University of Illinois/NCSA
# Open Source License
#
# Copyright (c) 2007-2018 University of Illinois at Urbana-Champaign.
# All rights reserved.
#
# Developed by:
#
#     LLVM Team
#
#     University of Illinois at Urbana-Champaign
#
#     http://llvm.org
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal with
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
#     * Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimers.
#
#     * Redistributions in binary form must reproduce the above copyright notice,
#       this list of conditions and the following disclaimers in the
#       documentation and/or other materials provided with the distribution.
#
#     * Neither the names of the LLVM Team, University of Illinois at
#       Urbana-Champaign, nor the names of its contributors may be used to
#       endorse or promote products derived from this Software without specific
#       prior written permission.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
# SOFTWARE.
#
#===------------------------------------------------------------------------===#

r"""
Refactor Tool Test Helper
=====================

This script runs a given tool in fix mode and verify fixes, messages or both.

Usage:
  check_refactor_tool.py [-resource-dir <resource-dir>] \
    <tool> <source-file> <check-name> <temp-file> \
    -- [optional extra arguments]

Example:
  // RUN: %check_refactor_tool c2cpp %s %t -- -std=c++17
"""

import argparse
import re
import subprocess
import sys


def write_file(file_name, text):
  with open(file_name, 'w') as f:
    f.write(text)
    f.truncate()

def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('-resource-dir')
  parser.add_argument('-FileCheck-path', default='FileCheck')
  parser.add_argument('tool_name')
  parser.add_argument('input_file_name')
  parser.add_argument('temp_file_name')

  args, extra_args = parser.parse_known_args()

  resource_dir = args.resource_dir
  tool_name = args.tool_name
  input_file_name = args.input_file_name
  temp_file_name = args.temp_file_name
  FileCheck = args.FileCheck_path

  extension = '.cpp'
  if (input_file_name.endswith('.c')):
    extension = '.c'
  if (input_file_name.endswith('.hpp')):
    extension = '.hpp'
  temp_file_name = temp_file_name + extension

  tool_extra_args = extra_args
  if len(tool_extra_args) == 0:
    tool_extra_args = ['--', '-std=@CPLUSPLUS_STD@'] if extension == '.cpp' \
                       else ['--']
  if resource_dir is not None:
    tool_extra_args.append('-resource-dir=%s' % resource_dir)

  with open(input_file_name, 'r') as input_file:
    input_text = input_file.read()

  has_check_fixes = input_text.find('CHECK-FIXES') >= 0
  has_check_messages = input_text.find('CHECK-MESSAGES') >= 0

  if not has_check_fixes and not has_check_messages:
    sys.exit('Neither CHECK-FIXES nor CHECK-MESSAGES found in the input')

  # Remove the contents of the CHECK lines to avoid CHECKs matching on
  # themselves.  We need to keep the comments to preserve line numbers while
  # avoiding empty lines which could potentially trigger formatting-related
  # checks.
  cleaned_test = re.sub('// *CHECK-[A-Z-]*:[^\r\n]*', '//', input_text)

  write_file(temp_file_name, cleaned_test)

  original_file_name = temp_file_name + ".orig"
  write_file(original_file_name, cleaned_test)

  args = [tool_name, temp_file_name, '-fix'] + tool_extra_args
  print('Running ' + repr(args) + '...')
  try:
    tool_output = \
        subprocess.check_output(args, stderr=subprocess.STDOUT).decode()
  except subprocess.CalledProcessError as e:
    print(tool_name + ' failed:\n' + e.output.decode())
    raise

  print('------------------------ output ----------------------------------\n' +
        tool_output +
        '\n------------------------------------------------------------------')

  try:
    diff_output = subprocess.check_output(
        ['diff', '-u', original_file_name, temp_file_name],
        stderr=subprocess.STDOUT)
  except subprocess.CalledProcessError as e:
    diff_output = e.output

  print('------------------------------ Fixes -----------------------------\n' +
        diff_output.decode() +
        '\n------------------------------------------------------------------')

  if has_check_fixes:
    try:
      subprocess.check_output(
          ['FileCheck', '-input-file=' + temp_file_name, input_file_name,
           '-check-prefix=CHECK-FIXES', '-strict-whitespace'],
          stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
      print('FileCheck failed:\n' + e.output.decode())
      raise

  if has_check_messages:
    messages_file = temp_file_name + '.msg'
    write_file(messages_file, tool_output)
    try:
      subprocess.check_output(
          [FileCheck, '-input-file=' + messages_file, input_file_name,
           '-check-prefix=CHECK-MESSAGES',
           '-implicit-check-not=warning:', '-implicit-check-not=error:'],
          stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
      print('FileCheck failed:\n' + e.output.decode())
      raise

if __name__ == '__main__':
  main()
